const { byteOrderType, bufferize, bytesToBinary } = require('./hex');

function handleFinsDataFn(data, arr, code = 3) {
  // console.log(data);

  let newData = {};

  if (code === 1) {
    arr.forEach((item, i) => {
      if (item.finsAddress) {
        newData[item.funId] = data[arr[i].offsetIdNum - arr[0].offsetIdNum];
      }
    });
  } else if (code === 3) {
    // 16进制值
    const hexValues = data;

    let newBuf = Buffer.alloc(hexValues.length * 2);

    hexValues.forEach((item, i) => {
      newBuf.writeInt16BE(String(item), i * 2);
    });

    // console.log('newBuf', newBuf);

    // 将16进制值转换为Buffer对象
    // const buffer = Buffer.from(hexValues);

    // 获得当前的报文
    // console.log('获得当前的报文', buffer);

    // byteOrderType 转字节序

    // 把获取到的报文经过字节序转换成实际数据
    // console.log(newBuf);
    newData = handleDataFn(newBuf, arr, code, 'fins');
  }

  return newData;
}

function handleDataFn(data = '', arr = [], code, mod = 'modbus') {
  // window.performance.clearMeasures();
  if (data) {
    if (arr && arr.length) {
      // const num = userData.slice(0,2);
      const modbusData = {};
      const arrData = data;
      const buffer = mod === 'modbus' ? Buffer.alloc(arrData.length / 2) : arrData;
      if (mod === 'modbus') {
        buffer.write(arrData, 'hex');
      }
      // console.log(buffer)
      if (code == 1 || code == 2) {
        let str = arrData + '';
        let leng = str.length / 2;
        let j = '';
        for (let i = 0; i < leng; i++) {
          let str2 = str.slice(2 * i, 2 * i + 2) || 0;
          str2 = parseInt(str2, 16).toString(2).padStart(8, '0');
          str2 = str2.split('').reverse().join('');
          j += str2;
        }

        let a = j + '';
        arr.forEach((item) => {
          // console.log('buffer',key,a[item.keyLeng / 2]);
          if (item.funId) {
            item.value = a[item.keyLeng / 2];

            modbusData[item.funId] = item.value;
          }
        });
      }

      if (code == 3 || code == 4) {
        arr.forEach((item, _key) => {
          const { type = null, decimalPlaces = 0 } = item;

          const keyLeng = item.keyLeng;

          switch (type) {
            case 'bit':
              let newBuf = null;

              if (item.byteOrder && (item.leng === 4 || item.leng === 8)) {
                newBuf = byteOrderType(
                  buffer.slice(keyLeng, keyLeng + item.leng),
                  item.byteOrder,
                  item.leng === '4' ? 'ABCD' : 'ABCDEFGH'
                );
              } else {
                newBuf = buffer.slice(keyLeng, keyLeng + item.leng);
              }

              if (item.byteOrder === 'CDAB' && item.leng === 2) {
                item.value = bytesToBinary(newBuf).split('').reverse();
              } else {
                item.value = bytesToBinary(newBuf).split('');
              }

              item.value = bytesToBinary(newBuf).split('');

              break;
            case 'int':
              item.value = buffer.readIntBE(keyLeng, 2);
              break;
            case 'int8':
              item.value = buffer.readInt8(keyLeng);
              break;
            case 'int16':
              item.value = buffer.readInt16BE(keyLeng);
              break;
            case 'uint16':
              item.value = buffer.readUInt16BE(keyLeng);
              break;
            case 'int32':
              if (item.byteOrder) {
                let newBuf = byteOrderType(
                  buffer.slice(keyLeng, keyLeng + item.leng),
                  item.byteOrder,
                  'ABCD'
                );

                item.value = newBuf.readInt32BE(0);
              } else {
                item.value = buffer.readInt32BE(keyLeng);
              }
              break;
            case 'uint32':
              if (item.byteOrder) {
                let newBuf = byteOrderType(
                  buffer.slice(keyLeng, keyLeng + item.leng),
                  item.byteOrder,
                  'ABCD'
                );
                item.value = newBuf.readUInt32BE(0);
              } else {
                item.value = buffer.readUInt32BE(keyLeng);
              }
              // item.value = buffer.readUInt32BE(keyLeng);
              break;
            case 'double':
              if (item.byteOrder) {
                let newBuf = byteOrderType(
                  buffer.slice(keyLeng, keyLeng + item.leng),
                  item.byteOrder,
                  'ABCDEFGH'
                );
                item.value = newBuf.readDoubleBE(0);
              } else {
                // console.log(keyLeng);
                try {
                  item.value = buffer.readDoubleBE(keyLeng);
                } catch (e) {
                  console.log(e);
                  // console.log(keyLeng);
                  // console.log(item);
                }
              }
              break;
            case 'float':
              if (item.byteOrder) {
                let newBuf = byteOrderType(
                  buffer.slice(keyLeng, keyLeng + item.leng),
                  item.byteOrder,
                  'ABCD'
                );
                item.value = newBuf.readFloatBE(0);
              } else {
                // console.log(keyLeng);
                try {
                  item.value = buffer.readFloatBE(keyLeng);
                } catch (e) {
                  console.log(e);
                  // console.log(keyLeng);
                  // console.log(item);
                }
              }
              break;
            case 'text':
              item.value = buffer.toString(item.encoding, keyLeng, keyLeng + item.leng);
              break;
            default:
              break;
          }

          if (
            Object.prototype.toString.call(item.value) !== '[object Array]' &&
            decimalPlaces !== 0
          ) {
            let zero = '';

            for (let i = 0; i < decimalPlaces; i++) {
              zero += '0';
            }

            const num = Number(1 + zero);

            if (!isNaN(num)) {
              item.value = item.value / num;
            }
          }

          // if (item.funId === '1ColorPhaseGet') {
          //   console.log('+++++', item);
          // }
          switch (item.computedType) {
            case 'plus':
              item.value = (item.value + item.computedNum).toFixed(decimalPlaces);
              break;
            case 'reduce':
              item.value = (item.value - item.computedNum).toFixed(decimalPlaces);
              break;
            case 'ride':
              // console.log('------------', item);
              item.value = (Math.round(item.value * item.computedNum * 100) / 100).toFixed(2);
              // console.log('------------', item);
              break;
            case 'except':
              item.value = (item.value / item.computedNum).toFixed(decimalPlaces);
              break;
            default:
              break;
          }

          let MNum = item.decimal === 3 ? 1000 : 100;

          let toFixedNum = !['', null, undefined].includes(item.decimal) ? item.decimal : 2;

          if (Object.prototype.toString.call(item.value) !== '[object Array]') {
            item.value = (Math.round(item.value * MNum) / MNum).toFixed(toFixedNum);
          }

          if (type === 'bit' && bitFunIdObj) {
            for (const i in bitFunIdObj) {
              modbusData[bitFunIdObj[i]] = item.value[i];
            }
          }

          if (item.funId) {
            modbusData[item.funId] = item.value;
          }
        });
      }

      return modbusData;

      // return arr;
    } else {
      return data;
    }
  }

  return data;
}

const dfFinsList = [];

class dfFins {
  options = null;

  reNum = 5;

  sock = null;

  sOptions = null;

  //  sockPing = false;

  isInit = false;

  num = 0;

  overTime = 100;

  //  pending = false;

  name = null;

  isOnLine = false;

  canRead = false;

  connectType = null;

  plcId = null;

  readList = null;

  constructor(options) {
    this.options = options;

    // console.log(options)

    // const { modbusOptions = null } = options;

    if (options) {
      const {
        connectOverTime = 100,
        reConnectNum = 5,
        name = null,
        plcId = null,
        readList = [],
      } = options;

      this.isInit = true;

      if (!this.sOptions) {
        this.sOptions = options;
      }

      if (connectOverTime) {
        this.overTime = connectOverTime;
      }

      if (reConnectNum) {
        this.reNum = reConnectNum;
      }

      if (name) {
        this.name = name;
      }

      if (plcId) {
        this.plcId = plcId;
      }

      if (readList) {
        this.readList = readList;
      }

      this.initFn(this.options);

      dfFinsList.push(this);
    } else {
      throw new Error('请传入配置参数');
    }
  }

  initFn(options = this.options) {
    const {
      port = null,
      host = null,
      connectOverTime = 5000,
      protocol = null,
      SA1 = null, // 本机ip最后一位
      DA1 = null, // plcip最后一位
    } = options;

    let oSa1 = SA1;

    let oDa1 = DA1;

    if (oSa1 === null) {
      const ip = require('ip').address();

      if (ip) {
        const ipList = ip.split('.');

        oSa1 = ipList[ipList.length - 1];
      } else {
        oSa1 = 0;
      }
    }

    if (oDa1 === null) {
      if (host) {
        const hostList = host.split('.');

        oDa1 = hostList[hostList.length - 1];
        // host.slice(host.length - 1)
      } else {
        oDa1 = 0;
      }
    }

    const fins = require('omron-fins');

    this.sock = fins.FinsClient(port, host, {
      timeout: connectOverTime,
      protocol,
      SA1: oSa1,
      DA1: oDa1,
    });

    this.sock.on('error', (error, msg) => {
      console.log('Error: ', error);

      console.log(msg);

      console.log(this.sock);

      if (!this.isInit) {
        this.reConnectFn(this.options);
      } else {
        if (this.num <= this.reNum) {
          this.num++;
          setTimeout(() => {
            this.reConnectFn(this.options, true);
          }, 1000);
        } else {
          this.isOnLine = false;
        }
      }
    });
    // Setting up our timeout listener
    this.sock.on('timeout', (host, msg) => {
      console.log('Timeout: ', host, msg);

      console.log(host);

      console.log(msg);

      console.log(this.sock);
    });

    this.sock.on('open', () => {
      console.log('打开端口');

      this.isOnLine = true;

      setTimeout(() => {
        getDataFn(readList);
      }, 500);
    });

    return new Promise((resolve) => {
      // 连接成功调用的事件
      let num = 0;

      let sockPingCommand = setInterval(async () => {
        num += 100;

        if (this.isOnLine || num >= this.overTime) {
          clearInterval(sockPingCommand);

          // console.log(this.isOnLine);

          this.isInit = true;

          resolve(true);
        }
      }, 100);
    });
  }

  reConnectFn(options = this.options, isConnent = false) {
    if (!this.isInit || isConnent) {
      if (!this.isInit) {
        this.isInit = true;
      }

      this.sock = null;

      this.initFn(options);

      // modbusStore.setIsOnLine(false);

      // modbusStore.setNotOnlineNameFn(this.name);

      this.isOnLine = false;

      process.send({
        type: 'isOnline',
        success: false,
        name: this.name,
      });
    }
  }

  async readFn(obj) {
    // console.log(obj)
    // console.log(this)
    // console.log(this.sock);

    // console.log(this.sock.isConnected());
    if (this.sock && obj) {
      const { address = null, num = null, addressList = null } = obj;

      let data = null;

      if (addressList && addressList.length) {
        this.sock.readMultiple(
          addressList,
          (err, msg) => {
            if (err) {
              console.log('SOMETHING WENT WRONG READING VALUES!!!!');

              data = false;

              // this.num = 0;

              // this.reConnectFn(this.sOptions,true);

              console.log('数据有误', msg);

              if (msg.timeout) {
                this.canRead = false;

                if (!this.isInit) {
                  this.reConnectFn(this.options);
                } else {
                  if (this.num <= this.reNum) {
                    this.num++;
                    setTimeout(() => {
                      this.reConnectFn(this.options, true);
                    }, 1000);
                  } else {
                    this.isOnLine = false;
                  }
                }
              }
            } else {
              data = msg.response.values || '';

              if (!this.canRead) {
                this.canRead = true;

                process.send({
                  type: 'isOnline',
                  success: true,
                  name: this.name,
                });
              }

              // if (!this.canRead || !modbusStore.getModbusIsOnLine) {
              //   this.canRead = true;

              //   modbusStore.setIsOnLine(true);

              //   modbusStore.removeNotOnlineNameFn(this.name);
              // }

              // console.log(values);
            }
          },
          { tagdata: '读取多个' + addressList.split(',') }
        );
      } else {
        this.sock.read(
          address,
          num,
          (err, msg) => {
            if (err) {
              console.log('SOMETHING WENT WRONG READING VALUES!!!!');

              data = false;

              // this.num = 0;

              // this.reConnectFn(this.sOptions,true);

              console.log('数据有误', msg);

              // console.log(address);

              // console.log(num);

              // console.log(this.canRead)

              process.send({
                type: 'read',
                success: false,
                name: this.name,
                err,
                msg,
              });

              if (msg.timeout) {
                this.canRead = false;

                process.send({
                  type: 'isOnline',
                  success: false,
                  name: this.name,
                });

                if (!this.isInit) {
                  this.reConnectFn(this.options);
                } else {
                  if (this.num <= this.reNum) {
                    this.num++;
                    setTimeout(() => {
                      this.reConnectFn(this.options, true);
                    }, 1000);
                  } else {
                    this.isOnLine = false;
                  }
                }
              }
            } else {
              data = msg.response.values || '';

              if (!this.canRead) {
                process.send({
                  type: 'isOnline',
                  success: true,
                  name: this.name,
                });
              }

              // console.log(this.canRead)

              // if (!this.canRead || !modbusStore.getModbusIsOnLine) {
              //   this.canRead = true;

              //   modbusStore.setIsOnLine(true);

              //   modbusStore.removeNotOnlineNameFn(this.name);
              // }
              // console.log(msg);
            }
          },
          { tagdata: `从${address}读取${num}个` }
        );
      }

      // sock.resume();
      // console.log(data)
      return new Promise((resolve) => {
        let num = 0;

        let sockPingCommand = setInterval(async () => {
          num += 100;

          if (data !== null || num >= this.overTime) {
            clearInterval(sockPingCommand);

            // this.pending = false;

            if (data === null) {
              process.send({
                type: 'read',
                success: false,
                name: this.name,
                data,
                isTimeOut: true,
              });
            }

            resolve(data);
          }
        }, 100);
      });
    } else {
      return false;
    }
  }

  async commandFn(obj, id) {
    if (this.sock && obj) {
      console.log(obj);
      let writeStatus = null;

      let data = obj.value;

      if (obj.type !== 'bool') {
        switch (obj.computedType) {
          case 'plus':
            data = (data - obj.computedNum).toFixed(obj.decimalPlaces || 0);
            break;
          case 'reduce':
            data = (data + obj.computedNum).toFixed(obj.decimalPlaces || 0);
            break;
          case 'ride':
            data = data / obj.computedNum;
            break;
          case 'except':
            data = (data * obj.computedNum).toFixed(obj.decimalPlaces || 0);
            break;
          default:
            break;
        }

        let decimal = 0;

        if (obj.decimalPlaces && obj.decimalPlaces !== 0) {
          decimal = decimalPlaces(obj.decimalPlaces);
          data = data * decimal;
        }

        let buffer = bufferize(data, obj.type);

        if (obj.byteOrder) {
          let newBuf = byteOrderType(buffer.slice(0, obj.leng), 'ABCD', obj.byteOrder);
          buffer = newBuf;
        }

        let arr = [];

        for (let index = 0; index < obj.leng / 2; index++) {
          arr.push(buffer.readInt16BE(index * 2));
        }
        console.log('单个数据的写入arr', arr);
        this.sock.write(
          `${obj.finsAddress}`,
          arr,
          (err, msg) => {
            // console.log(this.plcId);

            // console.log(err);

            // console.log(msg)

            if (err) {
              writeStatus = false;

              console.log('写入错误');

              console.log(err);

              console.log(msg);

              process.send({
                type: 'write',
                success: false,
                name: this.name,
                err,
                msg,
                id,
              });
            } else {
              writeStatus = true;

              process.send({
                type: 'write',
                success: true,
                name: this.name,
                id,
              });
            }
          },
          `write ${obj.finsAddress} ${arr}`
        );
      } else {
        this.sock.write(
          `${obj.finsAddress}`,
          [data],
          (err, msg) => {
            // console.log(this.plcId);

            // console.log(err);

            // console.log(msg)
            if (err) {
              writeStatus = false;

              console.log('写入错误');

              console.log(err);

              console.log(msg);

              process.send({
                type: 'write',
                success: false,
                name: this.name,
                err,
                msg,
                id,
              });
            } else {
              writeStatus = true;

              process.send({
                type: 'write',
                success: true,
                name: this.name,
                id,
              });
            }
          },
          `write ${obj.finsAddress} ${[data]}`
        );
      }

      // sock.resume();
      // console.log(data)
      return new Promise((resolve) => {
        let num = 0;

        let sockPingCommand = setInterval(async () => {
          num += 100;

          if (writeStatus !== null || num >= this.overTime) {
            clearInterval(sockPingCommand);

            if (writeStatus === null) {
              process.send({
                type: 'write',
                success: !!writeStatus,
                name: this.name,
                isTimeOut: true,
              });
            }

            // this.pending = false;
            // console.log(writeStatus)

            resolve(!!writeStatus);
          }
        }, 100);
      });
    } else {
      return false;
    }
  }

  closeSockFn() {
    if (this.sock) {
      this.sock.stop();
    }
  }
}

let fins = null;

// let intervalTime = null;

let intervalNum = 0;

let readList = [];

const getDataFn = async (list) => {
  const { isOnLine = false, sock = null, readFn = null } = fins || {};

  if (intervalNum > list.length - 1) {
    intervalNum = 0;
  }

  // console.log(isOnLine);

  // console.log(modbus.isOnLine);

  // console.log(modbus);

  // console.log(isOnLine);

  if (sock && isOnLine && readFn && list[intervalNum]) {
    let data = list[intervalNum];

    const { finsAddress = null, leng = null, code = null } = data;

    if (finsAddress && leng && code) {
      const res = await fins.readFn({
        address: finsAddress,
        num: leng,
      });

      if (res && res.length) {
        data = handleFinsDataFn(res, data.list, data.code);

        if (typeof data === 'object') {
          // process.send(data);
          process.send({
            type: 'read',
            success: true,
            name: this.name,
            data,
          });
          // modbusStore.updateNewData(data);
        }
      } else {
        console.log('error');
      }

      // return true;
    }

    if (intervalNum >= list.length - 1) {
      intervalNum = 0;
    } else {
      intervalNum++;
    }

    // intervalNum++;
  }

  getDataFn(list);

  // setTimeout(() => {
  //   getDataFn(list);
  // }, 1000);
};

const initFn = async (data) => {
  if (data) {
    const { options } = data;

    if (options) {
      fins = new dfFins({
        ...options,
        host: options.ip ? options.ip : options.host
      });

      readList = options.addressList || [];
    }
  }
};

const deleteFn = data => {
  console.log('关闭');

  // if(intervalTime){
  //   clearInterval(intervalTime);

  //   intervalTime = null;
  // }

  if (fins && fins.closeSockFn) {
    fins.closeSockFn();
  }

  fins = null;

  process.exit();
};

const doList = [];

let doPending = false;

const writeFn = async obj => {
  const { isOnLine = false, sock = null } = fins || {};

  const { writeObj = null, id = null } = obj;

  // console.log(obj)

  if (isOnLine && sock && obj) {
    if (doPending) {
      doList.push(obj);

      return false;
    } else {
      doPending = true;
    }

    // console.log(121212)

    await fins.commandFn(writeObj, id);

    doPending = false;

    if (doList && doList.length) {
      writeFn(doList[0]);

      doList.splice(0, 1);
    }
  }
};

const connectFn = () => {
  const {
    isOnLine = false,
    num = 0,
    reNum = 0,
    reConnectFn = null,
    sOptions = null,
    name = null
  } = fins;

  if (!isOnLine && reConnectFn && sOptions && num > reNum) {
    fins.num = 0;

    console.log(name + '重连');

    setTimeout(() => {
      fins.reConnectFn(sOptions, true);
    }, 1000);
  }
};

process.on('message', obj => {
  if (obj) {
    const { type = null, data = null } = obj;

    switch (type) {
      case 'init':
        initFn(obj);
        break;

      case 'write':
        writeFn(data);
        break;

      case 'connect':
        connectFn();
        break;

      case 'delete':
        deleteFn(obj);
        break;

      default:
        break;
    }
  }
});
